﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using CommandRunner.Contract;
using CommandRunner.Model;

namespace CommandRunner.Core
{
    public class Runner
    {
        public event CommandExecutionDelegate CommandCompleted;
        public event CommandExecutionDelegate CommandFailed;
        public event CommandExecutionDelegate CommandSkipped;

        private bool _continueRunning = true;
        private ICommandRunnerConfigurationManager _configurationManager;
        private ICommandRunnerClientDataTransport _dateTransport;
        private List<CommandResult> _executionTable = new List<CommandResult>();

        public Runner(ICommandRunnerConfigurationManager configurationManager, ICommandRunnerClientDataTransport dataTransport)
        {
            _configurationManager = configurationManager;
            _dateTransport = dataTransport;
        }

        public CommandExecutionStatus RunCommands()
        {
            foreach (SimpleCommand command in _configurationManager.CommandList)
            {
                CommandResult commandResult = new CommandResult
                {
                    Command = command,
                    CommandId = Guid.NewGuid(),
                    Status = CommandExecutionStatus.Pending
                };

                _executionTable.Add(commandResult);
            }

            foreach (CommandResult commandResult in _executionTable)
            {
                if (_continueRunning)
                {
                    RunCommand(commandResult);
                }
                else
                {
                    commandResult.Status = CommandExecutionStatus.Skipped;
                    if (CommandSkipped != null)
                        CommandSkipped(commandResult);
                }
            }

            while (!AllFinished())
            {
                System.Threading.Thread.Sleep(1000);
            }

            return CalculateSuccess();
        }

        public SimpleCommandList GetSkippedCommands()
        {
            SimpleCommandList skippedCommands = new SimpleCommandList();

            foreach (CommandResult commandResult in _executionTable)
            {
                if (commandResult.Status == CommandExecutionStatus.Skipped)
                    skippedCommands.Add(commandResult.Command);
            }

            return skippedCommands;
        }

        private void RunCommand(CommandResult commandResult)
        {
            if (commandResult.Command.Server != null)
            {
                Task.Factory.StartNew(() => RunRemoteCommand(commandResult), TaskCreationOptions.LongRunning);
            }
            else
            {
                ProcessCommand processCommand = new ProcessCommand(commandResult);
                processCommand.CommandFinished += ProcessFinishedCommand;
                processCommand.RunCommand();
            }

            if (commandResult.Command.Wait)
                while (commandResult.Status == CommandExecutionStatus.Executing) { Thread.Sleep(1000); }
        }

        private void RunRemoteCommand(CommandResult commandResult)
        {
            try
            {
                if (_dateTransport.StartCommandOnServer(commandResult))
                    if (_dateTransport.CheckCommandStatusOnServer(commandResult))
                        _dateTransport.GetCommandResultFromServer(commandResult);
            }
            catch (Exception e)
            {
                commandResult.Output = e.GetType() + e.Message + e.StackTrace;
                commandResult.Status = CommandExecutionStatus.Failure;
            }
            finally
            {
                ProcessFinishedCommand(commandResult);
            }
        }

        private void ProcessFinishedCommand(CommandResult commandResult)
        {
            if (commandResult.Status == CommandExecutionStatus.Success || !commandResult.Command.Monitor)
            {
                if (CommandCompleted != null)
                    CommandCompleted(commandResult);
            }
            else
            {
                _continueRunning = false;
                if (CommandFailed != null)
                    CommandFailed(commandResult);
            }
        }

        private bool AllFinished()
        {
            foreach (CommandResult commandResult in _executionTable)
            {
                if ((commandResult.Status == CommandExecutionStatus.Executing) || (commandResult.Status == CommandExecutionStatus.Pending))
                    return false;
            }

            return true;
        }

        private CommandExecutionStatus CalculateSuccess()
        {
            foreach (CommandResult commandResult in _executionTable)
            {
                if (commandResult.Status != CommandExecutionStatus.Success)
                    return CommandExecutionStatus.Failure;
            }

            return CommandExecutionStatus.Success;
        }
    }
}
